#if defined _convar_class_included
 #endinput
#endif
#define _convar_class_included

// todo: track previous default values 

static ArrayList _ConvarList;

enum struct convar_t
{
	ConVar cvar;
	char description[512];
	char defValue[512];
	char name[255];

	bool GetMin(float& input)
	{
		return this.cvar.GetBounds(ConVarBound_Lower, input);
	}
	bool GetMax(float& input)
	{
		return this.cvar.GetBounds(ConVarBound_Upper, input);
	}
	void SetMin(bool set, float& input = 0.0)
	{
		this.cvar.SetBounds(ConVarBound_Lower, set, input);
	}
	void SetMax(bool set, float& input = 0.0)
	{
		this.cvar.SetBounds(ConVarBound_Upper, set, input);
	}
}

methodmap Convar < ConVar
{
	public Convar(const char[] name, const char[] defaultValue, const char[] description = "",
					 int flags = 0, bool hasMin = false, float min = 0.0, bool hasMax = false, float max = 0.0, convar_t convar = {})
	{
		if(_ConvarList == null)
		{
			_ConvarList = new ArrayList(sizeof(convar_t));
		}

		ConVar cvar = CreateConVar(name, defaultValue, description, flags, hasMin, min, hasMax, max);

		convar_t savedValue;
		savedValue.cvar = cvar;
		strcopy(savedValue.description, 512, description);
		strcopy(savedValue.defValue, sizeof(savedValue.defValue), defaultValue);
		
		// Can't set default values :T
		savedValue.SetMin(hasMin, min);
		savedValue.SetMax(hasMax, max);

		convar = savedValue;

		_ConvarList.PushArray(savedValue);

		return view_as<Convar>(cvar);
	}

	public static bool CreateConfig(const char[] fileName = "", const char[] folder = "sourcemod", const bool clearWhenDone = true)
	{
		char localFolder[PLATFORM_MAX_PATH];
		FormatEx(localFolder, PLATFORM_MAX_PATH, "cfg/%s", folder);
		if(!DirExists(localFolder))
		{
			if(!CreateDirectory(localFolder, 755))
			{
				LogError("Error: Failed to create folder '%s'", localFolder);
			}
		}

		if(_ConvarList == null)
		{
			LogError("Error: No convars found. did you run .CreateConfig() before adding convars?");
			return false;
		}


		// Check if the file exists.
		char local[PLATFORM_MAX_PATH];
		if(fileName[0] == 0)
		{
			char pluginName[PLATFORM_MAX_PATH];
			GetPluginFilename(GetMyHandle(), pluginName, PLATFORM_MAX_PATH);

			ReplaceString(pluginName, PLATFORM_MAX_PATH, ".smx", "");
			ReplaceString(pluginName, PLATFORM_MAX_PATH, "\\", "/");

			int start = FindCharInString(pluginName, '/', true);

			FormatEx(local, PLATFORM_MAX_PATH, "cfg/%s/plugin.%s.cfg", folder, pluginName[start+1]);
		}
		else
		{
			FormatEx(local, sizeof(local), "cfg/%s/%s.cfg", folder, fileName);
		}
		bool fileExists = FileExists(local);
		if (!fileExists)
		{
			// Create first time file
			File file = OpenFile(local, "wt");
			if (file != null)
			{
				fileExists = true;

				// get the plugin name
				char pluginName[64];
				GetPluginFilename(GetMyHandle(), pluginName, 64);

				// Write warning
				file.WriteLine("// This file was auto-generated by KiD's Convar Class. Only plugin convars are allowed.");
				file.WriteLine("// ConVars for plugin \"%s\"\n\n", pluginName);

				// Loop through all of our convars
				for (int i = 0; i < _ConvarList.Length; ++i)
				{
					// get the current convar and description
					convar_t convar;
					_ConvarList.GetArray(i, convar);

					// don't write to file if flag is set
					if (convar.cvar.Flags & FCVAR_DONTRECORD != 0)
					{
						continue;
					}
					
					// make a copy of our description
					char descr[512];
					descr = convar.description;

					// format newlines as comments
					ReplaceString(descr, 512, "\n", "\n// ");

					// write the values and bounds to the file if they exist
					file.WriteLine("// %s", descr);

					// get the convar name and default value to write to file
					char name[64];
					convar.cvar.GetName(name, 64);

					file.WriteLine("// -");
					file.WriteLine("// Default: \"%s\"", convar.defValue);
					float x;
					if (convar.GetMin(x))
					{
						file.WriteLine("// Minimum: \"%02f\"", x);
					}
					if (convar.GetMax(x))
					{
						file.WriteLine("// Maximum: \"%02f\"", x);
					}
					file.WriteLine("%s \"%s\"\n", name, convar.defValue);
				}
				// end with newline
				file.WriteLine("");
				delete file;
			}
			else
			{
				// writing failed, notify developer.
				char pluginName[64];
				GetPluginFilename(GetMyHandle(), pluginName, 64);
				LogError("Failed to auto generate config for %s at '%s', make sure the directory has write permission.", pluginName, local);
				if(clearWhenDone)
				{
					delete _ConvarList;
				}
				return false;
			}
		}
		// file already exists, just update the description and defaults
		else
		{
			// open the file for reading
			File file = OpenFile(local, "r");
			// create a stringmap to hold our current convars.
			StringMap convars = new StringMap();

			char line[512];
			int currentLine = 0;
			while(!file.EndOfFile() && file.ReadLine(line, 512))
			{
				TrimString(line);

				++currentLine;
				// check if the line contains a valid statement
				if(line[0] != '/' && line[0] != '\n' && line[0] != 0)
				{
					char buffers[2][512];
					// only convars should be in here. which should contain convar [space] value.
					if(ExplodeString(line, " ", buffers, 2, 512, true) == 2)
					{
						// remove any trailing whitespace. should be none
						TrimString(buffers[0]);
						TrimString(buffers[1]);
						// remove the quotes from the values
						StripQuotes(buffers[0]);
						StripQuotes(buffers[1]);

						// since convars are only ever strings we store it as strings.
						convars.SetString(buffers[0], buffers[1]);
					}
					else
					{
						// someone put something in there that shouldn't be. Yell at the dev for doing stupid stuff.
						LogError("Error exploding convar string: '%s' on line: %i", line, currentLine);
					}
				}
			}
			// close our file
			delete file;
			// yay duplicate code
			// rewrite the cfg with old convars removed and new convars added.
			/* Attempt to recreate it */
			DeleteFile(local);
			file = OpenFile(local, "wt");
			if (file != null)
			{
				fileExists = true;

				char pluginName[64];
				GetPluginFilename(GetMyHandle(), pluginName, 64);

				file.WriteLine("// This file was auto-generated by KiD's Convar Class. Only plugin convars are allowed.");
				file.WriteLine("// ConVars for plugin \"%s\"\n\n", pluginName);

				float x;
				for (int i = 0; i < _ConvarList.Length; ++i)
				{
					convar_t convar;
					_ConvarList.GetArray(i, convar);

					if (convar.cvar.Flags & FCVAR_DONTRECORD != 0)
					{
						continue;
					}

					char descr[512];
					descr = convar.description;

					ReplaceString(descr, 512, "\n", "\n// ");
					file.WriteLine("// %s", descr);

					char name[64];
					convar.cvar.GetName(name, 64);

					file.WriteLine("// -");
					file.WriteLine("// Default: \"%s\"", convar.defValue);
					if (convar.GetMin(x))
					{
						file.WriteLine("// Minimum: \"%02f\"", x);
					}
					if (convar.GetMax(x))
					{
						file.WriteLine("// Maximum: \"%02f\"", x);
					}
					
					// only difference is that now we check for a stored value.
					char storedValue[512];
					if(convars.GetString(name, storedValue, 512))
					{
						file.WriteLine("%s \"%s\"\n", name, storedValue);
					}
					else
					{
						file.WriteLine("%s \"%s\"\n", name, convar.defValue);
					}
				}
				file.WriteLine("");
				delete file;
			}
			else
			{
				char pluginName[64];
				GetPluginFilename(GetMyHandle(), pluginName, 64);
				LogError("Failed to auto generate config for %s, make sure the directory has write permission.", pluginName);
				
				if(clearWhenDone)
				{
					delete _ConvarList;
				}

				delete convars;
				return false;
			}
		
			delete convars;
		}
		if(fileExists)
		{
			ServerCommand("exec \"%s\"", local[4]);
		}
		if(clearWhenDone)
		{
			delete _ConvarList;
		}
		return true;
	}

	public static void AutoExecConfig(const char[] fileName = "", const char[] folder = "sourcemod", const bool clearWhenDone = true)
	{
		if(Convar.CreateConfig(fileName, folder, clearWhenDone))
		{
			AutoExecConfig(false, fileName, folder);
		}
	}

	public void Close()
	{
		delete _ConvarList;
	}
}
